/*
 * Copyright © OpenAtom Foundation.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package io.iec.edp.caf.databaseobject.rtmanager;

import io.iec.edp.caf.boot.context.CAFContext;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import io.iec.edp.caf.database.Database;
import io.iec.edp.caf.databaseobject.DboDeployManager;
import io.iec.edp.caf.databaseobject.api.entity.*;
import io.iec.edp.caf.databaseobject.api.service.IDatabaseObjectRtService;
import io.iec.edp.caf.databaseobject.api.service.IDatabaseObjectTempTableService;
import io.iec.edp.caf.databaseobject.common.DatabaseObjectCommonUtil;
import io.iec.edp.caf.databaseobject.manager.DatabaseObjectServiceImpl;
import lombok.extern.slf4j.Slf4j;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLSyntaxErrorException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author : liu_wei
 * @date : 2022-06-22 08:42
 **/
@Slf4j
public class DatabaseObjectTempTableServiceImpl implements IDatabaseObjectTempTableService {

    private static Map<String, List<String>> sessionTempTableSqlCache = new ConcurrentHashMap<>();
    private static Map<String, List<String>> transactionTempTableSqlCache = new ConcurrentHashMap<>();
    private IDatabaseObjectRtService databaseObjectRtService = SpringBeanUtils.getBean(IDatabaseObjectRtService.class);


    @Override
    public TempTableContext createFixedTable(Database database, ScopeType type, String dboCode) {
        //固定列临时表SQL不变，进行缓存，可以提升性能
        List<String> sqlList = null;
        try {
            if (ScopeType.Session.equals(type)) {
                sqlList = getFixedTableWithSession(dboCode);
            } else {
                sqlList = getFixedTableWithTransaction(dboCode);
            }
            return execute(database, dboCode, type, sqlList);
        } catch (Exception e) {
            throw new RuntimeException("根据dboId创建临时表出错：{}",e);
        }
    }

    @Override
    public TempTableContext createNoFixedTable(Database database, ScopeType type, String dboCode) {
        List<String> sqlList = null;
        try {
            if (ScopeType.Session.equals(type)) {
                sqlList = getNoFixedTableWithSession(dboCode);
            } else if (ScopeType.Transactional.equals(type)) {
                sqlList = getNoFixedTableWithTransaction(dboCode);
            }
            return execute(database, dboCode, type, sqlList);
        } catch (Exception e) {
            throw new RuntimeException("根据dboId创建临时表出错：" + e);
        }
    }

    public TempTableContext execute(Database database, String dboCode, ScopeType type, List<String> sqlList) throws SQLException {
        String sessionId = CAFContext.current.getSessionId();
        String suffix = sessionId.substring(sessionId.length() - 10);
        DatabaseObjectServiceImpl service = new DatabaseObjectServiceImpl();
        DBInfo dbInfo = service.getDbInfo();
        String tempTableCode = null;
        if (DbType.Oracle.equals(dbInfo.getDbType()) || DbType.DB2.equals(dbInfo.getDbType()) || DbType.DM.equals(dbInfo.getDbType()) || DbType.Kingbase.equals(dbInfo.getDbType())) {
            tempTableCode = dboCode + "_" +type.toString();
        }else{
            tempTableCode = dboCode + suffix;
        }
        String tempTableName=tempTableCode;
        //设置成真正表名
        if (dbInfo.getDbType() == DbType.SQLServer) {
            tempTableCode = "#" + tempTableCode;
            tempTableName = "#" + tempTableCode;
        }
        String querySql=DatabaseObjectCommonUtil.tempTableIsExist(dbInfo,tempTableName);
        if(!"".equals(querySql)){
            ResultSet resultSet=database.query(querySql);
            if(dbInfo.getDbType() == DbType.SQLServer){
                if(resultSet.next()){
                    if(resultSet.getString("id")!=null && !"".equals(resultSet.getString("id"))){
                        TempTableContext context = new TempTableContext();
                        context.setTableName(tempTableName);
                        return context;
                    }
                }
            }else{
                if(resultSet.next()){
                    TempTableContext context = new TempTableContext();
                    context.setTableName(tempTableName);
                    return context;
                }
            }
        }
        for (String sql : sqlList) {
            if(dbInfo.getDbType() == DbType.SQLServer){
                if(sql.contains("#")){
                    String code ="#"+dboCode;
                    sql = sql.replaceAll("(?i)" + code, tempTableName);
                }else{
                    sql = sql.replaceAll("(?i)" + dboCode, tempTableName);
                    String index_name = sql.split(" index ")[1].split(" ")[0];
                    sql = sql.replaceAll("(?i)" + index_name,index_name+ suffix);
                }
            }else{
                if(sql.toLowerCase().contains(" index ")){
                    sql = sql.replaceAll("(?i)" + dboCode, tempTableName);
                    String index_name = sql.split(" index ")[1].split(" ")[0];
                    sql = sql.replaceAll("(?i)" + index_name,index_name+suffix);
                }else{
                    sql = sql.replaceAll("(?i)" + dboCode, tempTableName);
                }
            }
            log.debug("执行的临时表创建SQL为：" + sql);
            try{
                if (ScopeType.Session.equals(type)) {
                    database.execute(sql);
                } else {
                    database.execute(sql);
                }
            }catch (Exception e){
                if(!e.getMessage().toLowerCase().contains("duplicate key name")) {
                    throw e;
                }
            }
        }
        TempTableContext context = new TempTableContext();
        context.setTableName(tempTableName);
        return context;
    }

    /**
     * 获取会话级固定列临时表SQL
     *
     * @param dboCode dboCode
     * @return SQL
     */
    private List<String> getFixedTableWithSession(String dboCode) {
        if (sessionTempTableSqlCache.containsKey(dboCode)) {
            return sessionTempTableSqlCache.get(dboCode);
        }
        List<String> sqlList = getTemplateSql(dboCode);
        sessionTempTableSqlCache.put(dboCode, sqlList);
        return sqlList;
    }

    /**
     * 获取会话级非固定列临时表SQL
     *
     * @param dboCode dboCode
     * @return SQL
     */
    private List<String> getNoFixedTableWithSession(String dboCode) {
        return getTemplateSql(dboCode);
    }

    /**
     * 获取事务级固定列临时表SQL
     *
     * @param dboCode dboCode
     * @return SQL
     */
    private List<String> getFixedTableWithTransaction(String dboCode) {
        if (transactionTempTableSqlCache.containsKey(dboCode)) {
            return transactionTempTableSqlCache.get(dboCode);
        }
        List<String> sqlList = getTemplateSql(dboCode);
        List<String> sqlListNew = new ArrayList<>();
        for (String sql : sqlList) {
            if(sql.contains("#")){
                sqlListNew=sqlList;
            }
            if (sql.contains("ON COMMIT PRESERVE ROWS")) {
                sqlListNew.add(sql.replaceAll("(?i)ON COMMIT PRESERVE ROWS", "ON COMMIT DELETE ROWS"));
            }
        }
        transactionTempTableSqlCache.put(dboCode, sqlListNew);
        return sqlListNew;
    }

    /**
     * 获取事务级非固定列临时表SQL
     *
     * @param dboCode dboCode
     * @return SQL
     */
    private List<String> getNoFixedTableWithTransaction(String dboCode) {
        List<String> sqlList = getTemplateSql(dboCode);
        List<String> sqlListNew = new ArrayList<>();
        for (String sql : sqlList) {
            if(sql.contains("#")){
                sqlListNew=sqlList;
            }
            if (sql.contains("ON COMMIT PRESERVE ROWS")) {
                sqlListNew.add(sql.replaceAll("(?i)ON COMMIT PRESERVE ROWS", "ON COMMIT DELETE ROWS"));
            }
        }
        return sqlListNew;
    }

    private List<String> getTemplateSql(String dboCode) {
        AbstractDatabaseObject object = databaseObjectRtService.getDatabaseObjectByCode(dboCode);
        DatabaseObjectServiceImpl service = new DatabaseObjectServiceImpl();
        DBInfo dbInfo = service.getDbInfo();
        List<AbstractDatabaseObject> objects = new ArrayList<>();
        objects.add(object);
        DboDeployManager dboDeployManager = new DboDeployManager();
        Map<String, List<String>> sqlMap = dboDeployManager.getTempTablesCreateSql(objects, dbInfo);
        return sqlMap.get(object.getCode().toLowerCase());
    }
}
